package mini;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

import mini.expr.Element;
import mini.expr.Expr;
import mini.statement.ReturnException;


public class Proc {

    private ParamList parameterlist;
    private StatementList stmtlist;
    private boolean usesHeap;
    
	public Proc(ParamList pl, StatementList sl) {
        parameterlist = pl;
        stmtlist = sl;
    }

    public boolean isUsesHeap() {
		return usesHeap;
	}

	public void setUsesHeap(boolean usesHeap) {
		this.usesHeap = usesHeap;
	}

    public Element apply(HashMap<String, Element> nametable, HashMap<String, Proc> functiontable, LinkedList var, ExpressionList expressionlist) {
        //System.out.println("Executing Proceedure");
        HashMap<String, Element> newnametable = new HashMap<String, Element>();

        // bind parameters in new name table
        // we need to get the underlying List structure that the ParamList uses...
        Iterator<String> p = parameterlist.getParamList().iterator();
        Iterator<Expr> e = expressionlist.getExpressions().iterator();

        if (parameterlist.getParamList().size() != expressionlist.getExpressions().size()) {
            System.out.println("Param count does not match");
            System.exit(1);
        }
        
        if (usesHeap) {
	        for(String key : nametable.keySet()) {
	        	newnametable.put(key, nametable.get(key));
	        }
        }
        
        while (p.hasNext() && e.hasNext()) {

            // assign the evaluation of the expression to the parameter name.
            newnametable.put(p.next(), e.next().eval(nametable, functiontable, var));
        //System.out.println("Loading Nametable for procedure with: "+p+" = "+nametable.get(p));

        }
        
        // evaluate function body using new name table and 
        // old function table
        // eval statement list and catch return
        //System.out.println("Beginning Proceedure Execution..");
        try {
            stmtlist.eval(newnametable, functiontable, var);
        } catch (Exception result) {
        	if( result instanceof ReturnException ) {
            	return ((ReturnException)result).getElement();
        	}
        	
        	throw new RuntimeException(result);
        }
        System.out.println("Error:  no return value");
        System.exit(1);
        // need this or the compiler will complain, but should never
        // reach this...
        return null;
    }
}